<template>
  <div class="knowledge-graph-page">
    <canvas ref="starfield" class="starfield-canvas"></canvas>
    <div ref="knowledgeGraph" class="knowledge-graph-container"></div>
    <!-- 行星装饰 -->
    <img class="planet planet1" src="https://pngimg.com/d/planet_PNG35.png" />
    <img class="planet planet2" src="https://pngimg.com/d/planet_PNG35.png" />
    <!-- 左侧触发器按钮 -->
    <div class="trigger-btn" @click="drawerOpen = true">
      <span>&gt;</span>
    </div>
    <!-- 遮罩层 -->
    <div v-if="drawerOpen" class="drawer-mask" @click="drawerOpen = false"></div>
    <!-- 自定义毛玻璃抽屉 -->
    <div class="custom-drawer" :class="{ open: drawerOpen }">
      <div class="drawer-content" @click.stop>
        <div class="drawer-close" @click="drawerOpen = false">&times;</div>
        <div class="drawer-form">
          <div class="drawer-tabs">
            <div
              :class="['drawer-tab', tabKey === 'select' && 'active']"
              @click="tabKey = 'select'"
            >
              选择已有知识大纲
            </div>
            <div :class="['drawer-tab', tabKey === 'input' && 'active']" @click="tabKey = 'input'">
              自主填写知识大纲
            </div>
            <div :class="['drawer-tab', tabKey === 'list' && 'active']" @click="tabKey = 'list'">
              知识图谱列表
            </div>
          </div>
          <div v-if="tabKey === 'select'" class="drawer-pane">
            <select v-model="formState.generateTeachId" class="drawer-select">
              <option v-for="item in teachDataList" :key="item.id" :value="item.id">
                {{ item.title }}
              </option>
            </select>
              <textarea
              v-if="formState.generateTeachId"
              v-model="selectedOutline"
              rows="16"
              class="drawer-textarea"
              placeholder="请选择知识大纲..."
            ></textarea>
          </div>
          <div v-else-if="tabKey === 'input'" class="drawer-pane">
            <textarea
              v-model="formState.outline"
              rows="16"
              :maxlength="20000"
              class="drawer-textarea"
              placeholder="请输入知识大纲..."
            ></textarea>
          </div>
          <div v-else-if="tabKey === 'list'" class="drawer-pane drawer-graph-list">
            <ul>
              <li v-for="record in dataList" :key="record.id" @dblclick="handleSwitchGraph(record)">
                <div class="graph-title">{{ record.title }}</div>
                <div class="graph-outline">{{ record.description }}</div>
              </li>
            </ul>
          </div>
          <div v-if="tabKey !== 'list'" style="text-align: right; margin-top: 24px">
            <button class="drawer-btn" @click="handleSubmit">生成知识图谱</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, reactive, computed } from 'vue'
import * as echarts from 'echarts'
import { listGenerateTeachVoByPage } from '@/api/generateTeachController'
import { message } from 'ant-design-vue'
import { addKnowledgeGraph, listKnowledgeGraphByPage } from '@/api/knowledgeGraphController'
import { a } from '@kousum/semi-ui-vue/dist/markdownRender/components'

const starfield = ref<HTMLCanvasElement | null>(null)
const knowledgeGraph = ref<HTMLDivElement | null>(null)
let ctx: CanvasRenderingContext2D | null = null
let animationId: number
const dataList = ref<API.KnowledgeGraphVO[]>([])
const queryParams = reactive<API.GenerateTeachQueryDTO>({
  current: 1,
  pageSize: 5, //固定只能展示3个数据
})
const fetchData = async () => {
  try {
    const res = await listKnowledgeGraphByPage({
      ...queryParams,
    })
    if (res.data.data && res.data.code === 0) {
      dataList.value = res.data.data.records ?? []
    } else {
      message.error('获取知识图谱列表失败' + res.data.msg)
    }
  } catch (e) {
    message.error('获取知识图谱列表失败' + e)
  }
}

const teachDataList = ref<API.GenerateTeachVO[]>([])
//搜索条件 复用分页查询的接口
const searchParams = reactive<API.GenerateTeachQueryDTO>({
  current: 1,
  pageSize: 10, //固定只能展示3个数据
})
const fetchTeachData = async () => {
  try {
    const res = await listGenerateTeachVoByPage({
      ...searchParams,
    })
    if (res.data.data && res.data.code === 0) {
      teachDataList.value = res.data.data.records ?? []
    } else {
      message.error('获取已有大纲列表失败' + res.data.msg)
    }
  } catch (e) {
    message.error('获取已有大纲列表失败' + e)
  }
}

//页面加载自动获取数据
onMounted(() => {
  fetchTeachData()
  fetchData()
})

// 星空参数
const stars: any[] = []
const numStars = 300
const meteors: any[] = []
const maxMeteors = 5

function createStars(width: number, height: number) {
  stars.length = 0
  for (let i = 0; i < numStars; i++) {
    stars.push({
      x: Math.random() * width,
      y: Math.random() * height,
      radius: Math.random() * 1.5,
      speed: Math.random() * 0.5 + 0.1,
    })
  }
}

function createMeteor(width: number, height: number) {
  if (meteors.length < maxMeteors) {
    meteors.push({
      x: Math.random() * width,
      y: (Math.random() * height) / 2,
      length: Math.random() * 100 + 50,
      speed: Math.random() * 5 + 3,
      opacity: Math.random() * 0.7 + 0.3,
      angle: Math.PI / 4,
    })
  }
}

function drawStars(width: number, height: number) {
  if (!ctx) return
  stars.forEach((star) => {
    ctx.save()
    ctx.globalAlpha = 0.5 + 0.5 * Math.sin(Date.now() / 500 + star.x)
    ctx.beginPath()
    ctx.arc(star.x, star.y, star.radius, 0, Math.PI * 2)
    ctx.fillStyle = 'white'
    ctx.shadowColor = 'white'
    ctx.shadowBlur = 8
    ctx.fill()
    ctx.restore()
    star.y += star.speed
    if (star.y > height) {
      star.y = 0
      star.x = Math.random() * width
    }
  })
}

function drawMeteors(width: number, height: number) {
  if (!ctx) return
  for (let i = meteors.length - 1; i >= 0; i--) {
    const meteor = meteors[i]
    ctx.beginPath()
    ctx.lineWidth = 2
    ctx.strokeStyle = `rgba(255,255,255,${meteor.opacity})`
    ctx.moveTo(meteor.x, meteor.y)
    ctx.lineTo(
      meteor.x - meteor.length * Math.cos(meteor.angle),
      meteor.y + meteor.length * Math.sin(meteor.angle),
    )
    ctx.stroke()
    meteor.x += meteor.speed * Math.cos(meteor.angle)
    meteor.y += meteor.speed * Math.sin(meteor.angle)
    if (meteor.x < 0 || meteor.y > height) {
      meteors.splice(i, 1)
    }
  }
}

function animate() {
  if (!starfield.value || !ctx) return
  const width = starfield.value.width
  const height = starfield.value.height
  ctx.clearRect(0, 0, width, height)
  drawStars(width, height)
  drawMeteors(width, height)
  createMeteor(width, height)
  animationId = requestAnimationFrame(animate)
}

function resizeCanvas() {
  if (!starfield.value) return
  starfield.value.width = window.innerWidth
  starfield.value.height = window.innerHeight
  createStars(window.innerWidth, window.innerHeight)
}

// 侧边抽屉表单相关
const drawerOpen = ref(false)
const tabKey = ref('select')
const selectedOutline = computed(()=>{
  const currentIndex = teachDataList.value.findIndex(td => td.id === formState.generateTeachId)
  return teachDataList.value[currentIndex].content
})
const formState = reactive<API.KnowledgeGraphAddRequest>({
  generateTeachId:'',
  outline:''
})
// 生成知识图谱
const handleSubmit = async ()=> {
  // TODO: 调用AI接口生成知识图谱
  try {
    const res = await addKnowledgeGraph({
      ...formState
    })
    if (res.data.data && res.data.code === 0) {
      await fetchData()
      message.success('知识图谱生成成功')
    } else {
      message.error('知识图谱生成失败')
    }
  } catch (e) {
    message.error('知识图谱生成失败')
  }finally {
    drawerOpen.value = false
  }

}
const currentData = ref<API.GraphOption>()
let myChart: echarts.ECharts | null = null

function handleSwitchGraph(record: API.KnowledgeGraphVO) {
  currentData.value = record.graphOption
  // 重新渲染知识图谱
  if (myChart && currentData.value) {
    const option = generateChartOption(currentData.value)
    myChart.setOption(option, true) // true表示完全替换
  }

  drawerOpen.value = false
}

// 提取图表配置生成函数
function generateChartOption(data: API.GraphOption) {
  // 颜色池
  const colorPool = [
    '#40a9ff',
    '#36cfc9',
    '#ff85c0',
    '#ffd666',
    '#6B5B95',
    '#88B04B',
    '#F7CAC9',
    '#ff9800',
    '#ff3d00',
    '#b37feb',
    '#5cdbd3',
    '#ff7875',
    '#ffc069',
  ]

  // 自动识别核心节点（名字包含"核心"或"核心知识点"）
  const isCoreNode = (name: string) => {
    return name.includes('核心')
  }

  // 获取节点类型
  const getNodeType = (name: string) => {
    if (isCoreNode(name)) {
      return 'core'
    }
    return 'normal'
  }

  // 图标映射
  const iconMap: { [key: string]: string } = {
    core: 'diamond', // 核心节点使用菱形
    normal: 'circle', // 普通节点使用圆形
  }

  // 构造ECharts option
  return {
    backgroundColor: 'transparent',
    tooltip: { trigger: 'item', formatter: '{b}' },
    series: [
      // 主知识图谱
      {
        type: 'graph',
        layout: 'force',
        force: {
          repulsion: 1200,
          edgeLength: [100, 200],
        },
        roam: true,
        draggable: true,
        data:
          data.nodes?.map((node: any, idx: number) => {
            const type = getNodeType(node.name)
            const symbol = iconMap[type] || 'circle'
            const colorIdx = idx % colorPool.length
            return {
              ...node,
              symbol,
              symbolSize: type === 'core' ? 90 : 55,
              itemStyle:
                type === 'core'
                  ? {
                      color: {
                        type: 'radial',
                        x: 0.5,
                        y: 0.5,
                        r: 0.7,
                        colorStops: [
                          { offset: 0, color: '#fffbe6' },
                          { offset: 0.5, color: '#ff9800' },
                          { offset: 1, color: '#ff3d00' },
                        ],
                      },
                      shadowBlur: 40,
                      shadowColor: '#ff9800',
                    }
                  : {
                      color: colorPool[colorIdx],
                      shadowBlur: 18,
                      shadowColor: colorPool[colorIdx],
                    },
              label: {
                show: true,
                position: 'top',
                backgroundColor: 'rgba(0,0,0,0.5)',
                borderRadius: 8,
                padding: [6, 14],
                color: '#fff',
                fontWeight: 'bold',
                fontSize: type === 'core' ? 20 : 15,
                textShadowColor: type === 'core' ? '#ff9800' : colorPool[colorIdx],
                textShadowBlur: type === 'core' ? 10 : 8,
                rich: {
                  a: { color: '#fff', fontWeight: 'bold', fontSize: type === 'core' ? 20 : 15 },
                  b: { color: colorPool[colorIdx], fontSize: 12 },
                },
                formatter: (params: any) => `{a|${params.name}}`,
              },
            }
          }) || [],
        links: data.edges || [],
        lineStyle: {
          color: 'source',
          width: 3,
          curveness: 0.3,
          shadowColor: '#fff',
          shadowBlur: 8,
        },
        edgeSymbol: ['none', 'arrow'],
        edgeSymbolSize: 16,
        emphasis: {
          focus: 'adjacency',
          scale: true, // 悬浮放大
          itemStyle: {
            borderColor: '#fff',
            borderWidth: 4,
            shadowBlur: 60,
            shadowColor: '#fff',
          },
          label: {
            fontSize: 22,
            color: '#fffbe6',
            textShadowColor: '#ff9800',
            textShadowBlur: 20,
          },
        },
      },
      // 波纹涟漪效果（effectScatter，hover节点时显示）
      {
        type: 'effectScatter',
        coordinateSystem: null, // 让ECharts自动跟随graph布局
        zlevel: 2,
        rippleEffect: {
          period: 2,
          scale: 6,
          brushType: 'stroke',
        },
        symbolSize: 90,
        data: [], // 初始无数据，hover时动态填充
      },
    ],
  }
}

onMounted(() => {
  resizeCanvas()
  ctx = starfield.value?.getContext('2d') || null
  animate()
  window.addEventListener('resize', resizeCanvas)

  // ECharts 知识图谱
  if (knowledgeGraph.value) {
    myChart = echarts.init(knowledgeGraph.value)
    // 假设这是API返回的动态数据
    const mockData = {
      nodes: [
        { name: '套接字的定义与通信作用' },
        { name: 'TCP与UDP协议的本质差异' },
        { name: 'TCP的可靠传输原理（三次握手/重传机制）' },
        { name: 'UDP的无连接通信原理' },
        { name: '多线程并发中的线程同步机制（如锁机制）' },
        { name: '用户权限控制的核心原理：基于角色的访问控制逻辑' },
        { name: '客户端-服务器通信的基本交互模型' },
        { name: '请求-响应处理机制的实现原理' },
        { name: '网络通信中的数据格式定义与解析方法' },
        { name: '网络通信异常处理机制（如错误码处理、异常捕获）' },
        { name: '跨平台Socket API兼容性原理' },
        { name: '网络性能测试与抓包分析技术原理（如Wireshark）' },
        { name: '测试报告撰写的标准化结构与内容要求' },
      ],
      edges: [
        { source: '套接字的定义与通信作用', target: 'TCP与UDP协议的本质差异' },
        { source: 'TCP与UDP协议的本质差异', target: 'TCP的可靠传输原理（三次握手/重传机制）' },
        { source: 'TCP与UDP协议的本质差异', target: 'UDP的无连接通信原理' },
        { source: '套接字的定义与通信作用', target: '多线程并发中的线程同步机制（如锁机制）' },
        { source: '客户端-服务器通信的基本交互模型', target: '请求-响应处理机制的实现原理' },
        { source: '客户端-服务器通信的基本交互模型', target: '网络通信中的数据格式定义与解析方法' },
        {
          source: '用户权限控制的核心原理：基于角色的访问控制逻辑',
          target: '客户端-服务器通信的基本交互模型',
        },
        {
          source: '网络通信异常处理机制（如错误码处理、异常捕获）',
          target: '套接字的定义与通信作用',
        },
        { source: '跨平台Socket API兼容性原理', target: '套接字的定义与通信作用' },
        {
          source: '网络性能测试与抓包分析技术原理（如Wireshark）',
          target: '客户端-服务器通信的基本交互模型',
        },
        {
          source: '测试报告撰写的标准化结构与内容要求',
          target: '网络性能测试与抓包分析技术原理（如Wireshark）',
        },
      ],
    }

    // 初始化图表
    const option = generateChartOption(mockData)
    myChart.setOption(option)

    // 波纹hover动画逻辑
    myChart.on('mouseover', function (params) {
      if (params.seriesType === 'graph' && params.dataType === 'node') {
        const node = params.data
        // 设置effectScatter数据为当前节点
        myChart.setOption({
          series: [
            {}, // 保持graph不变
            {
              data: [
                {
                  ...node,
                  // effectScatter会自动跟随graph节点位置
                },
              ],
            },
          ],
        })
      }
    })
    myChart.on('mouseout', function (params) {
      if (params.seriesType === 'graph' && params.dataType === 'node') {
        // 清空effectScatter
        myChart.setOption({
          series: [{}, { data: [] }],
        })
      }
    })

    window.addEventListener('resize', () => {
      myChart.resize()
    })
  }
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', resizeCanvas)
  if (animationId) cancelAnimationFrame(animationId)
})
</script>
<style scoped>
.knowledge-graph-page {
  width: 100vw;
  height: 100vh;
  position: relative;
  overflow: hidden;
  background: url('@/assets/star1.png') center center/cover no-repeat;
}
.starfield-canvas {
  position: fixed;
  width: 100vw;
  height: 100vh;
  z-index: 0;
  top: 0;
  left: 0;
  pointer-events: none;
}
.knowledge-graph-container {
  width: 100vw;
  height: 100vh;
  position: fixed;
  z-index: 1;
  top: 0;
  left: 0;
}
.planet {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  opacity: 0.85;
}
.planet1 {
  left: 5vw;
  bottom: 3vh;
  width: 220px;
  animation: planet-rotate 60s linear infinite;
}
.planet2 {
  right: 8vw;
  bottom: 8vh;
  width: 140px;
  animation: planet-rotate 90s linear infinite reverse;
}
@keyframes planet-rotate {
  0% {
    transform: rotate(0deg) scale(1);
  }
  100% {
    transform: rotate(360deg) scale(1.05);
  }
}
.trigger-btn {
  position: fixed;
  left: 0;
  top: 50%;
  z-index: 1002;
  width: 40px;
  height: 80px;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  border-radius: 0 20px 20px 0;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: left 0.3s cubic-bezier(0.23, 1, 0.32, 1);
  font-size: 32px;
  user-select: none;
  transform: translateY(-50%);
  box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
}
.trigger-btn:hover {
  left: 8px;
  background: #1677ff;
}
.custom-drawer {
  position: fixed;
  top: 270px;
  left: 0;
  width: 40vw;
  max-width: 520px;
  min-height: 520px;
  max-height: 600px;
  z-index: 1002;
  background: rgba(20, 24, 40, 0.5);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 2px 0 24px rgba(0, 0, 0, 0.25);
  border-radius: 0 18px 18px 0;
  transform: translateX(-100%);
  transition: transform 0.4s cubic-bezier(0.23, 1, 0.32, 1);
  display: flex;
  align-items: flex-start;
  justify-content: flex-start;
  overflow: visible;
}
.custom-drawer.open {
  transform: translateX(0);
}
.drawer-content {
  width: 100%;
  min-height: 320px;
  max-height: 600px;
  overflow-y: auto;
  padding: 32px 24px 24px 24px;
  position: relative;
  box-sizing: border-box;
  background: transparent;
  border-radius: 0 18px 18px 0;
}
.drawer-close {
  position: absolute;
  right: 18px;
  top: 18px;
  font-size: 28px;
  color: #fff;
  cursor: pointer;
  z-index: 2;
  opacity: 0.7;
  transition: opacity 0.2s;
}
.drawer-close:hover {
  opacity: 1;
}
.drawer-form {
  margin-top: 8px;
}
.drawer-tabs {
  display: flex;
  border-bottom: 1.5px solid #e0e0e0;
  margin-bottom: 18px;
}
.drawer-tab {
  padding: 8px 18px;
  cursor: pointer;
  color: #888;
  font-weight: 500;
  border-bottom: 2px solid transparent;
  transition:
    color 0.2s,
    border-color 0.2s;
}
.drawer-tab.active {
  color: #1677ff;
  border-bottom: 2px solid #1677ff;
  background: rgba(255, 255, 255, 0.05);
}
.drawer-pane {
  margin-top: 12px;
}
.drawer-select,
.drawer-textarea {
  width: 100%;
  font-size: 16px;
  border-radius: 6px;
  border: 1px solid #d9d9d9;
  padding: 8px 12px;
  margin-bottom: 8px;
  background: rgba(255, 255, 255, 0.15);
  color: #222;
  outline: none;
  box-sizing: border-box;
  resize: vertical;
}
.drawer-btn {
  background: #1677ff;
  color: #fff;
  border: none;
  border-radius: 6px;
  padding: 8px 24px;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.2s;
}
.drawer-btn:hover {
  background: #0958d9;
}
.drawer-mask {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(0, 0, 0, 0.18);
  z-index: 1001;
  transition: opacity 0.3s;
  opacity: 1;
}
.drawer-graph-list ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.drawer-graph-list li {
  background: rgba(255, 255, 255, 0.08);
  border-radius: 8px;
  margin-bottom: 12px;
  padding: 12px 14px;
  cursor: pointer;
  transition:
    background 0.2s,
    box-shadow 0.2s;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.drawer-graph-list li:hover {
  background: rgba(22, 119, 255, 0.12);
  box-shadow: 0 4px 16px rgba(22, 119, 255, 0.08);
}
.graph-title {
  font-weight: 600;
  color: #1677ff;
  font-size: 16px;
  margin-bottom: 4px;
}
.graph-outline {
  color: #888;
  font-size: 13px;
  white-space: pre-line;
}
</style>
